package decoder

import (
	"bufio"
	"errors"
	"net"
	"socket-goim-decoder-demo/encoding/binary"
)

const (
	// Size
	_PackageLen         = 4
	_HeaderLen          = 2
	_ProtocolVersionLen = 2
	_OperationLen       = 4
	_SequenceIdLen      = 4
	_AllHeaderLen       = _PackageLen + _HeaderLen + _ProtocolVersionLen + _OperationLen + _SequenceIdLen

	// Offset
	_PackageOffset         = 0
	_HeaderOffset          = _PackageLen
	_ProtocolVersionOffset = _HeaderOffset + _HeaderLen
	_OperationOffset       = _ProtocolVersionOffset + _ProtocolVersionLen
	_SequenceIdOffset      = _OperationOffset + _OperationLen
)

type Protocol struct {
	Ver  int16
	Op   int32
	Seq  int32
	Body []byte
}

type Decoder interface {
	ReadMessage() (*Protocol, error)
}

func NewDecoder(conn net.Conn) Decoder {
	return &decoder{conn: conn, reader: bufio.NewReader(conn)}
}

type decoder struct {
	conn   net.Conn
	reader *bufio.Reader
}

func (d *decoder) ReadMessage() (*Protocol, error) {
	allHeader, err := d.reader.Peek(_AllHeaderLen)

	if err != nil {
		return nil, err
	}

	_, err = d.reader.Discard(_AllHeaderLen)

	if err != nil {
		return nil, err
	}

	pkgLen := binary.BigEndian.Int32(allHeader[_PackageOffset : _PackageOffset+_PackageLen])
	headLen := binary.BigEndian.Int16(allHeader[_HeaderOffset : _HeaderOffset+_HeaderLen])

	proto := &Protocol{
		Ver: binary.BigEndian.Int16(allHeader[_ProtocolVersionOffset : _ProtocolVersionOffset+_ProtocolVersionLen]),
		Op:  binary.BigEndian.Int32(allHeader[_OperationOffset : _OperationOffset+_OperationLen]),
		Seq: binary.BigEndian.Int32(allHeader[_SequenceIdOffset : _SequenceIdOffset+_SequenceIdLen]),
	}

	if headLen != _AllHeaderLen {
		return nil, errors.New("error proto format")
	}

	bodyLen := pkgLen - int32(_HeaderLen)

	if bodyLen <= 0 {
		return proto, nil
	}

	body, err := d.reader.Peek(int(bodyLen))

	if err != nil {
		return nil, err
	}

	_, err = d.reader.Discard(int(bodyLen))

	if err != nil {
		return nil, err
	}

	proto.Body = body

	return proto, nil
}
